/* $RCSFile: ADNBaseLicenseProvider.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.ServerControls/LicenceControls/ADNBaseLicenseProvider.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:30  christian.surieux
 *   Added Log to All cs files
 */

// ADNBaseLicenseProvider.cs
// Server Controls and Components
//

using System;
using System.Diagnostics;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Globalization;
using System.Web;
using System.Web.Configuration;

namespace ADNCRM.ServerControls
{
    /// <summary>
    /// Base class file for Addon Nice's licenses
    /// </summary>
    public class ADNBaseLicenseProvider : LicenseProvider 
	{

		/// <summary>
		/// Caching system to speed up performance keeping already read licenses
		/// </summary>
        private static readonly ADNBaseLicenseCollector LicenseCollector = new ADNBaseLicenseCollector();

		protected virtual ADNBaseLicense CreateLicense(Type type, string key) 
		{
			return new ADNBaseLicense(type, key);
		}

		/// <summary>
		/// Try to get a lcence for a context, type and object instance
        /// If the licence is not found in the cache, create an instance and validate it
		/// </summary>
		/// <param name="context"></param>
		/// <param name="type"></param>
		/// <param name="instance"></param>
		/// <param name="allowExceptions"></param>
		/// <returns></returns>
        public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions) 
		{
			ADNBaseLicense license = null;
			string errorMessage = null;
            if ( LicenseTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicense Type: {0}", type),LicenseTraceSwitch.Sw.Info);
			if (context.UsageMode == LicenseUsageMode.Designtime) 
			{
				license = CreateLicense(type, String.Empty);
			}
			else 
			{
				license = LicenseCollector.GetLicense(type);
                if ( LicenseTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicense Type: {0}, LicenseCollector: {1}", type,(license!=null)),LicenseTraceSwitch.Sw.Info);
 
				if (license == null) 
				{
					string licenseData = GetLicenseData(type);
                    if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicense Type: {0}, LicenseData: {1}", type, !string.IsNullOrEmpty(licenseData) ),LicenseTraceSwitch.Sw.Info);
                    if ( !string.IsNullOrEmpty(licenseData) ) 
					{

						if (ValidateLicenseData(type, licenseData)) 
						{
							ADNBaseLicense newLicense = CreateLicense(type, licenseData);
                            if ( LicenseTraceSwitch.Sw.TraceVerbose )
                                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicense Type: {0}, newLicense: {1}", type, newLicense),LicenseTraceSwitch.Sw.Info);

							if (ValidateLicense(newLicense, out errorMessage)) 
							{
								license = newLicense;
								LicenseCollector.AddLicense(type, license);
							}
						}
					}
				}
				else 
				{
					if (ValidateLicense(license, out errorMessage) == false) 
					{
						license = null;
					}
				}
			}

			if (allowExceptions && (license == null)) 
			{
				if (errorMessage == null) 
				{
					throw new LicenseException(type);
				}
				else 
				{
					throw new LicenseException(type, instance, errorMessage);
				}
			}

			return license;
		}

        /// <summary>
        /// Read licese data for the type transmitted with a stream reader
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
		protected virtual string GetLicenseData(Type type) 
		{
			string licenseData = null;
			Stream licenseStream = null;

			try 
			{
				licenseStream = GetLicenseDataStream(type);

				if (licenseStream != null) 
				{
					StreamReader sr = new StreamReader(licenseStream);

					licenseData = sr.ReadLine();
				}
			}
			finally 
			{
				if (licenseStream != null) 
				{
					licenseStream.Close();
					licenseStream = null;
				}
			}

			return licenseData;
		}


        /// <summary>
        /// Get the license folder from settings, works for winforms or asp taking app.exe.config or web.config
        /// using the Appsetings section with key LicenseFilesPath
        /// </summary>
        /// <returns></returns>
        protected virtual string GetLicensePath()
        {
            string fullPath = string.Empty; // license files folder
            string relativePath = string.Empty;
            const string strLicKey = "LicenseFilesPath";
            
            if (HttpContext.Current == null)
            {
                // get It from application settings
                relativePath = ConfigurationManager.AppSettings[strLicKey];
                if ( LicenseTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicensePath from App.exe.config : {0}", relativePath),LicenseTraceSwitch.Sw.Info);
                if (string.IsNullOrEmpty(relativePath))
                    relativePath = "licenses";
                fullPath = System.IO.Path.GetFullPath(relativePath);
            }
            else
            {
                // Get it from web config
                relativePath = WebConfigurationManager.AppSettings[strLicKey];
                if ( LicenseTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicensePath from web.config : {0}", relativePath),LicenseTraceSwitch.Sw.Info);
                if (string.IsNullOrEmpty(relativePath))
                    relativePath = "~/licenses";
                fullPath = HttpContext.Current.Server.MapPath(relativePath);
            }
            return fullPath;
        }
        
        /// <summary>
        /// Open a stream from the license file corresponding to this type
        /// License files are in the folder Licenses under the web root for a web site or the exe running folder
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
		protected virtual Stream GetLicenseDataStream(Type type) 
		{
            string prodTypeName = type.Name; // the type indicate the product
            string assemblyPart = type.Assembly.GetName().Name; // the assembly is not used
            string versionPart = type.Assembly.GetName().Version.ToString(); // version is the assembly version(where the type is defined )
            string licensesFile     =   string.Empty;
            string fullPath = GetLicensePath(); // license files folder
			try 
			{
                string[] files = System.IO.Directory.GetFiles(fullPath,"*.lic");
            
                // Search for licence file
                foreach (string file in files)
                {
                    string[] sparts = file.Split(new char[] { '_' },StringSplitOptions.RemoveEmptyEntries);
                //string outputFile = string.Format("{0}_{1}_{2}_{3}_{4}_{5}.lic", new object[] { Company, prodName, outputType, outputRights, outputAssembly, outputVersion.Replace('*','x') });
                    if ( sparts.GetLength(0) < 6 )
                        continue;
                    string prodName = sparts[1];
                    string tmpstr   =   sparts[5];
                    int idx = tmpstr.IndexOf(".lic");
                    if (idx == -1)
                        continue;
                    string outputVersion = tmpstr.Substring(0,idx);
                    if ( prodTypeName != prodName )
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (prodTypeName: {1}) != (prodName: {2})", new object[] { file, prodTypeName, prodName }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    string[] vparts = versionPart.Split(new char[] { '.' },StringSplitOptions.RemoveEmptyEntries);
                    string[] vparts2 = outputVersion.Split(new char[] { '.' },StringSplitOptions.RemoveEmptyEntries);
                    if (vparts.GetLength(0) != vparts2.GetLength(0) || vparts.GetLength(0) < 4)
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (vparts.GetLength(0): {1}) != (vparts2.GetLength(0): {2})", new object[] { file, vparts.GetLength(0), vparts2.GetLength(0) }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    if (vparts[0] != vparts2[0])
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (vparts[0]: {1}) != (vparts2[0]: {2})", new object[] { file, vparts[0], vparts2[0] }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    if (vparts[1] != vparts2[1])
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (vparts[1]: {1}) != (vparts2[1]: {2})", new object[] { file, vparts[1], vparts2[1] }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    if (vparts[2] != vparts2[2] && vparts2[2].ToLower() != "x")
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (vparts[2]: {1}) != (vparts2[2]: {2})", new object[] { file, vparts[2], vparts2[2] }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    if (vparts[3] != vparts2[3] && vparts2[3].ToLower() != "x")
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream File: {0}, (vparts[3]: {1}) != (vparts2[3]: {2})", new object[] { file, vparts[3], vparts2[3] }),LicenseTraceSwitch.Sw.Info);
                        continue;
                    }
                    licensesFile = file;
                    // try this file
                    Stream strm = new FileStream(licensesFile, FileMode.Open, FileAccess.Read, FileShare.Read);
                    //Ok
                    if (strm != null)
                    {
                        if ( LicenseTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream Type: {0}, assemblyPart: {1}, versionPart: {2}, FilePath: {3}", new object[] { type, assemblyPart, versionPart, licensesFile }),LicenseTraceSwitch.Sw.Info);
                        return strm;
                    }
                }
			}
			catch(Exception ex)
			{
                if ( LicenseTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ADNBaseLicenseProvider GetLicenseDataStream Type: {0}, assemblyPart: {1}, versionPart: {2}, FilePath: {3}, ex: {4}", new object[] { type, assemblyPart, versionPart, licensesFile,ex }),LicenseTraceSwitch.Sw.Info);
            }
            // no license fileOk, echec
            if ( LicenseTraceSwitch.Sw.TraceError )
                Trace.WriteLine("[V]ADNBaseLicenseProvider GetLicenseDataStream fileOk but license not Ok",LicenseTraceSwitch.Sw.Info);
            return null;
		}

		protected virtual bool ValidateLicense(ADNBaseLicense license, out string errorMessage) 
		{
			errorMessage = null;
			return true;
		}

		protected virtual bool ValidateLicenseData(Type type, string licenseData) 
		{
			string licenseKey = type.Name;
			return String.Compare(licenseKey, licenseData, true, CultureInfo.InvariantCulture) == 0;
		}


		private sealed class ADNBaseLicenseCollector 
		{

			private IDictionary _collectedLicenses;

			public ADNBaseLicenseCollector() 
			{
				_collectedLicenses = new HybridDictionary();
			}

			public void AddLicense(Type objectType, ADNBaseLicense license) 
			{
				if (objectType == null) 
				{
					throw new ArgumentNullException("objectType");
				}
				if (license == null) 
				{
					throw new ArgumentNullException("objectType");
				}

				_collectedLicenses[objectType] = license;
			}

			public ADNBaseLicense GetLicense(Type objectType) 
			{
				if (objectType == null) 
				{
					throw new ArgumentNullException("objectType");
				}

				if (_collectedLicenses.Count == 0) 
				{
					return null;
				}

				return (ADNBaseLicense)_collectedLicenses[objectType];
			}

			public void RemoveLicense(Type objectType) 
			{
				if (objectType == null) 
				{
					throw new ArgumentNullException("objectType");
				}

				_collectedLicenses.Remove(objectType);
			}
		}
	}
}
